/**
This file is part of fbfo.

fbfo is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

fbfo is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with fbfo.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * depends: prototype
 * recommends: scriptacalous (with effects.js)
 *
 * settings: (these are optional settings)
 *   fbfo.options.appearEffect = Effect.Appear (choose your favorite)
 *   fbfo.options.disappearEffect = Effect.Fade
 *   fbfo.options.alertTimeout = 10 (to overwrite default timeout for alert feedbacks)
 * 
 * manual usage:
 *   fbfo.start("divid", "Hello, World!");
 *   fbfo.end("divid", "Hello, World!");
 *   (a div can have multiple messages, you can start and stop them
 *   independently)
 *
 * alert like usage:
 *   fbfo.alert("divid", "Hello, World!");
 *   fbfo.alert("divid", "Hello, World!", 30);
 *   (duration param is optional)
 *
 * resetting div:
 *   fbfo.reset("divid");
 *   (clean message queue and disappear div.)
 */

fbfo = function() {
	/* private scope */

	/**
	 * data structure to hold separate feedback information for separate target
	 * doms.
	 *
	 * data structure is so:
	 * queue = [{
	 *		id : "<TARGET_DOM_ID>",
	 *		feedbacks : [{
	 * 			text :"<FEEDBACK_TEXT>",
	 *			count : <COUNTOFSAMEFEEDBACKINQUEUE>
	 *		}, ...]
	 *	}...]
	 */
	var queue = [];

	/**
	 * creates a new queue element for a new dom, adds an initial feedback with
	 * given text and pushs it to queue.
	 *
	 * @param tid target dom id.
	 * @param ftext feedback text to be added initally
	 *
	 * @return new element added to queue.
	 */
	var addTarget = function(tid, ftext) {
		var newFeedBack = {
			text : ftext,
			count : 0
		};
		var newTarget = {
			id : tid,
			feedbacks : [newFeedBack]
		};
		queue.push(newTarget);
		return newTarget;
	};

	/**
	 * removes queue element related with target dom id.
	 *
	 * @param tid target dom id.
	 *
	 * @return void
	 */
	var removeTarget = function(tid) {
		var removalTarget = queue.find(function(target) {
			return target.id == tid;
		});
		queue.splice(queue.indexOf(removalTarget), 1);
	};

	/**
	 * adds a new feedback with given text to given feedbacks array.
	 *
	 * @param feedbacks Array that holds separate feedbacks belongs to one dom
	 * target.
	 * @param feedback one element of feedbacks array that is expired.
	 *
	 * @return added feedback
	 */
	var addFeedBack = function(feedbacks, ftext) {
		var fback = {
				text : ftext,
				count : 1
			};
		feedbacks.push(fback);
		return fback;
	};

	/**
	 * removes expired feedback from given feedbacks array.
	 *
	 * @param feedbacks Array that holds separate feedbacks belongs to one dom
	 * target.
	 * @param feedback one element of feedbacks array that is expired.
	 *
	 * @return void
	 */
	var removeFeedBack = function(feedbacks, feedback) {
		feedbacks.splice(feedbacks.indexOf(feedback), 1);
	};

	/**
	 * gets all feedbacks and the feedback with searched text, creates a new
	 * queue element automatically if there is no previos record.
	 *
	 * @param tid target dom id.
	 * @param ftext feedback text to be added initally
	 *
	 * @return Object with structure:
	 * {
	 *		all : [<feedbacks array belongs to dom withtid>]
	 *		found : {<found feedback which matches with ftext>}
	 * }
	 */
	var getFeedBacks = function(tid, ftext) {
		var target = false, previosTarget = false;
		previosTarget = queue.find(function(target) {
			return target.id == tid;
		});
		target = previosTarget || addTarget(tid, ftext);
		return {
			all : target.feedbacks,
			found : target.feedbacks.find(function(fback) {
				return fback.text == ftext;
			})
		};
	};

	/**
	 * clears feedback target dom, and fills it with feedback information
	 * coming from queue.
	 *
	 * @param tid target dom id.
	 * @param ftext feedback text to be added initally
	 *
	 * @return void
	 */
	var refreshDom = function(tid, fbacks) {
		if (!$(tid)) {
			return false;;
		}
		while($(tid).hasChildNodes()) {
			$(tid).removeChild($(tid).firstChild);
		}
		fbacks.each(function(fback) {
			$(tid).appendChild(document.createTextNode(fback.text));
			$(tid).appendChild(document.createElement("br"));
		});
		$(tid).removeChild($(tid).lastChild); //remove last br
	};

	/**
	 * makes target dom visible with scriptaculous effects
	 *
	 * @param tid target dom id.
	 * @return boolean operation result
	 */
	var appear = function(tid) {
		if (!$(tid)) {
			return false;
		}
		if (fbfo.options.appearEffect) {
			return fbfo.options.appearEffect(tid);
		}
		Element.show($(tid));
		return true;
	};

	/**
	 * makes target dom invisible with scriptaculous effects.
	 *
	 * @param tid target dom id.
	 * @return boolean operation result
	 */
	var disappear = function(tid) {
		if (!$(tid)) {
			return false;
		}
		if (fbfo.options.disappearEffect) {
			return fbfo.options.disappearEffect(tid);
		}
		Element.hide($(tid));
		return true;
	};

	return {

		version: "1.0",

		/* public scope */

		/**
		 * stores default options to collect magic numbers and etc. used in
		 * public, and customizable function pointers.
		 *
		 * alertTimeout => expiration time for feedback alerts.
		 *
		 * appearEffect => custom function for feedback appearance effect,
		 * target id is passed as parameter to this function.
		 *
		 * disappearEffect => custom function for feedback disappearance
		 * effect, target id is passed as parameter to this function.
		 */
		options : {
			alertTimeout : 5,
			appearEffect : false,
			disappearEffect : false
		},

		/**
		 * starts a new feedback, if already started one at the same dom
		 * target, adds new feedback to queue for this dom.
		 *
		 * @param targetid target dom id.
		 * @param feedbacktext feedback text to be added initally
		 *
		 * @return void
		 */
		start : function start(targetid, feedbacktext) {
			var feedbacks = getFeedBacks(targetid, feedbacktext);
			if (feedbacks.found) {
				feedbacks.found.count++;
			} else {
				feedbacks.found = addFeedBack(feedbacks.all, feedbacktext);
			}
			refreshDom(targetid, feedbacks.all);
			appear(targetid);
		},

		/**
		 * checks for feedback queue if there is any left, if so just
		 * decreases counter of this feedback belongs to that dom target by 1,
		 * if queue is empty ends feedback animation.
		 *
		 * @param targetid target dom id.
		 * @param feedbacktext feedback text to be added initally
		 *
		 * @return void
		 */
		end : function end(targetid, feedbacktext) {
			var feedbacks = getFeedBacks(targetid, feedbacktext);
			if (feedbacks.found) {
				feedbacks.found.count--;
				if (feedbacks.found.count == 0) {
					removeFeedBack(feedbacks.all, feedbacks.found);
				}
			}
			if (feedbacks.all.length > 0) {
				refreshDom(targetid, feedbacks.all)
				return;
			}
			disappear(targetid);
		},

		/**
		 * prints expirable feedback.
		 *
		 * @param targetid target dom id.
		 * @param feedbacktext feedback text to be added initally
		 * @param duration expire interval in seconds.
		 *
		 * @return void
		 */
		alert : function alert(targetid, feedbacktext, duration) {
			var seconds = duration || fbfo.options.alertTimeout;
			var feedbackend = function() { fbfo.end(targetid, feedbacktext); };
			fbfo.start(targetid, feedbacktext);
			setTimeout(feedbackend, seconds * 1000);
		},

		/**
		 * cleans queue for the target, disappears feedback if it's
		 * active.
		 *
		 * @param targetid target dom id.
		 *
		 * @return void
		 */
		reset : function reset(targetid) {
			if (!targetid) {
				//needed to copy queue for destructive loop
				var tmp = $A(queue);
				tmp.each(function (q) {
					removeTarget(q.id);
					disappear(q.id);
				});
				return;
			}
			removeTarget(targetid);
			disappear(targetid);
		}
	};
}();
